/*
 * Copyright 2008 Slava Chernyak
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License. 
 */
package com.chernyak.DataAdapter;

import java.util.*;

import com.chernyak.DataAdapter.types.*;

/**
 * This node in the AST represents the body of the data adapter
 * declaration
 * 
 * @author Slava Chernyak
 */
public class DataAdapterDeclBody extends ParserNode {
	ArrayList<TypeDecl> fields;
	
	/**
	 * Constructor for the CUP-generated parser
	 * @param fields
	 * @param ln
	 */
	public DataAdapterDeclBody(
			ArrayList<TypeDecl> fields,
			int ln) {
		super(ln);
		this.fields = fields;
	}
	
	/**
	 * Gets the string representation of this data adapter body
	 */
	public String toString() {
		StringBuilder sb = new StringBuilder();
		sb.append("{\n");
		for(TypeDecl d : fields) {
			sb.append(d.toString() + "\n");
		}
		sb.append("}\n");
		return sb.toString();
	}
	
	/**
	 * Determines if this data adapter is comparable. A data adapter is
	 * comparable if any fields in this data adapter body are declared 
	 * as comparable
	 * @return True if this data adapter is comparable. False otherwise
	 */
	public boolean isComparable() {
		for (TypeDecl d : fields) {
			if (d.isCompare()) {
				return true;
			}
		}
		return false;
	}
	
	/**
	 * Determines the parsable status of this data adapter. For more information
	 * on possible parsable status types, see @link ParsableStatus
	 * @param globalSymbolTable The global symbol table
	 * @return The parsable status of this data adapter
	 */
	public ParsableStatus getParsable(Map<String, GlobalSymbolProperties> globalSymbolTable) {
		// simple parsability: first 1 to n-1 elements are primitives, strings, 
		// or fixed size arrays of primitives or strings. last element may be a 
		// params array of primitives or strings.
		
		//TODO: implement
		for (int i = 0; i < fields.size(); i++) {
			TypeDecl d = fields.get(i);
			if (i == (fields.size() - 1)) {
				if (d instanceof PrimitiveTypeDecl || d instanceof StringDecl) {
					return ParsableStatus.FULLY_PARSABE;
				} else if (d instanceof ArrayedTypeDecl) {
					ArrayedTypeDecl ad = (ArrayedTypeDecl)d;
					if (ad.getArrayDims() == 1 && 
							(ad.getArrayedType() instanceof PrimitiveTypeDecl || 
									ad.getArrayedType() instanceof StringDecl)) {
						if (ad.isFixedSize()) {
							return ParsableStatus.FULLY_PARSABE;
						} else {
							return ParsableStatus.PARAMS_PARSABLE;
						}
					} else {
						return ParsableStatus.NOT_PARSABLE;
					}
					
				} else {
					return ParsableStatus.NOT_PARSABLE;
				}
				
			} else {
				if (d instanceof PrimitiveTypeDecl || d instanceof StringDecl) {
					continue;
				} else if (d instanceof ArrayedTypeDecl) {
					ArrayedTypeDecl ad = (ArrayedTypeDecl)d;
					if (ad.isFixedSize() &&  ad.getArrayDims() == 1 && 
							(ad.getArrayedType() instanceof PrimitiveTypeDecl || 
									ad.getArrayedType() instanceof StringDecl)) {
						continue;
					} else {
						return ParsableStatus.NOT_PARSABLE;
					}
				} else {
					return ParsableStatus.NOT_PARSABLE;
				}
					
			}
		}
		
		return ParsableStatus.FULLY_PARSABE;
		
		
//		// to be parsable the following must be true:
//		//
//		//  for n field declarations, declarations 1 to n-1 must be fully 
//		//  parsable. 
//		//  
//		// if the last declaration is fully parsable, then the whole class is
//		// fully parsable.
//		//
//		// if the last declaration is params parsable, then the whole class is
//		// params parsable 
//		
//		for (int i = 0; i < fields.size(); i++) {
//			TypeDecl d = fields.get(i);
//			if (i == fields.size() - 1) {
//				// last field
//				if (d instanceof ArrayedTypeDecl) {
//					ArrayedTypeDecl ad = (ArrayedTypeDecl)d;
//					if (! ad.isFixedSize()) {
//						return ParsableStatus.PARAMS_PARSABLE;
//					}
//				} else if (d instanceof CompositeTypeDecl) {
//					GlobalSymbolProperties p = 
//						globalSymbolTable.get(d.getTypeName());
//					if ( p == null ) {
//						throw new CompilerException("Cannot find symbol '" + 
//								d.getTypeName() + "' in global symbols", ln);
//					}
//					if (p.getParsable() == ParsableStatus.PARAMS_PARSABLE) {
//						return ParsableStatus.PARAMS_PARSABLE;
//					}
//				}
//				
//			} else {
//				// check the 1 to n-1 fields
//				if (d instanceof ArrayedTypeDecl) {
//					ArrayedTypeDecl ad = (ArrayedTypeDecl)d;
//					if (! ad.isFixedSize()) {
//						return ParsableStatus.NOT_PARSABLE;
//					}
//				} else if (d instanceof CompositeTypeDecl) {
//					GlobalSymbolProperties p = 
//						globalSymbolTable.get(d.getTypeName());
//					if ( p == null ) {
//						throw new CompilerException("Cannot find symbol '" + 
//								d.getTypeName() + "' in global symbols", ln);
//					}
//					if (p.getParsable() != ParsableStatus.FULLY_PARSABE) {
//						return ParsableStatus.NOT_PARSABLE;
//					}
//				}
//			}
//		}
	}
	
	/**
	 * @see ParserNode#typecheck(Map, List, String)
	 */
	public void typecheck(Map<String, GlobalSymbolProperties> gs, List<String> s, String packageContext) {
		for (TypeDecl d : fields) {
			d.typecheck(gs, s, packageContext);
		}
		
		int comparableItems = 0;
		for (TypeDecl d : fields) {
			if (d.isCompare()) {
				comparableItems++;
			}
		}
	}
	
	/**
	 * @see ParserNode#lowerEquals(StringBuilder)
	 */
	public void lowerEquals(StringBuilder sink) {
		for (TypeDecl d : fields) {
			d.lowerEquals(sink);
		}
	}
	
	public void lowerToString(StringBuilder sink) {
		for (TypeDecl d : fields) {
			d.lowerToString(sink);
		}
	}
	
	public void lowerHashCode(StringBuilder sink) {
		for (TypeDecl d : fields) {
			d.lowerHashCode(sink);
		}
	}
	
	public void lowerDeclarations(StringBuilder sink) {
		for (TypeDecl d : fields) {
			d.lowerDeclarations(sink);
		}
	}
	
	public void lowerReadWCG(StringBuilder sink) {
		for (TypeDecl d : fields) {
			d.lowerReadWCG(sink);
		}
	}
	
	public void lowerWriteWCG(StringBuilder sink) {
		for (TypeDecl d : fields) {
			d.lowerWriteWCG(sink);
		}
	}
	
	public void lowerComparisonWCG(StringBuilder sink) {
		for (TypeDecl d : fields) {
			if (d.isCompare()) {
				d.lowerComparisonWCG(sink);
			}
		}
	}
	
	public void lowerParse(StringBuilder sink) {
		for (TypeDecl d : fields) {
			d.lowerParse(sink);
		}
	}
}
